package groep1;

import groep1.filter.StartFilter;
import groep1.graph.GraphFrame;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import bluej.debugger.*;
import bluej.debugmgr.inspector.FieldInfo;
import bluej.debugmgr.objectbench.ObjectWrapper;

@Kuleuven
public class ObjectList {
	
	private ObjectList() {}
	
	private static ObjectList objectList;
	private HashSet<ObjectRepresentation> objects = new HashSet<ObjectRepresentation>();
	private static ArrayList<String> ignoredTypes = new ArrayList<String>(Arrays.asList("Int", "String", "Long"));
	
	/**
	 * Static method to get the only instance of this class. Only one instance
	 * can be created because this class is a Singleton.
	 * 
	 * @return	The instance of this class.
	 */
	public static ObjectList getInstance() {
		if (objectList==null)
			objectList = new ObjectList();
		return objectList;
	}
	
	// TODO verplaatsen naar ObjectRepresentation?
	private void setDirectObjects(ObjectRepresentation objectRepresentation, boolean inObjectBench) {
		objects.add(objectRepresentation);
		
		if(!objectRepresentation.isInBench() && inObjectBench)
				objectRepresentation.setOnBench();
		
		List<DebuggerField> list = objectRepresentation.getObject().getFields();
		
    	if (!list.isEmpty()) { // minstens een veld, mogelijk een obj
    		for (DebuggerField debuggerField : list) { // alle velden overlopen van parent obj
    			if (isValidDebuggerfield(debuggerField)) { // veld is een obj
    				DebuggerObject newObject = debuggerField.getValueObject(null);
    				ObjectRepresentation newObjectRepresentation = getObjectRepresentation(newObject, false);
    				newObjectRepresentation.addReference(debuggerField, objectRepresentation);
    				newObjectRepresentation.setName(debuggerField.getName());
    				if (newObject != null && !newObject.isArray()) {
	    				setDirectObjects(newObjectRepresentation,false);
    				} else { // voor arrays nu al toevoegen
    					objects.add(newObjectRepresentation);
    					newObjectRepresentation.setModifier(debuggerField);
    				}
    			}
    		}
		}
	}
	
	/**
	 * Check whether the given debuggerField is a valid debuggerField.
	 * 
	 * @param	field
	 * 			| The debuggerField that must be validated.
	 * @return	true if the field is not null and it contains a debuggerobject.
	 */
	private boolean isValidDebuggerfield(DebuggerField field){
		try {
			field.getValueObject(null);
		} catch (NullPointerException e) {
			return false;
		}
		DebuggerObject object = field.getValueObject(null);
		if(object instanceof DebuggerObject && object != null){
			System.out.println();
			String[] className = object.getClassName().split("\\.");
			if(!ignoredTypes.contains(className[className.length-1])){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Reset the objectList and fills him again with the object of the given wrapperList
	 * and the objects in their fields
	 */
	public void update(List<ObjectWrapper> wrapperList) {
		objects.clear();
		for (ObjectWrapper objectWrapper : wrapperList) {
			ObjectRepresentation objectRepresentation = getObjectRepresentation(objectWrapper.getObject(), true);
			objectRepresentation.setName(objectWrapper.getName());
			setDirectObjects(objectRepresentation,true);
		}
//		printObjectList();
//		System.out.println("-------------------------------------");
		GraphFrame.getInstance().applyFilter();
//		HashMap<String, List<FieldInfo>> result = filter("niet k:{Boot}");
//		GraphFrame.getInstance().getGraphComponent().addTestObjects(result);
//		GraphFrame.getInstance().setVisible(true);

	}
	
	/**
	 * Prints out the complete objectList
	 */
//	private void printObjectList() {
//		for (ObjectRepresentation objectRepresentation : objects) {
//			System.out.println(objectRepresentation.getObject().getClassName());
//			System.out.println(objectRepresentation.isInBench());
//			System.out.println("Is refered by:");
//			objectRepresentation.printFieldReferences();
//			System.out.println("---");
//		}
//	}
	
	/**
	 * Returns the an objectRepresentation of the given debuggerObject. If the object is already
	 * in the objectList you get his objectRepresentation if not a new one will be created
	 * (But not added to the list).
	 * 
	 * @param 	object
	 * 			| The object from which you want the objectRepresentation
	 * 
	 * @param	inObjectBench
	 * 			| If the object is on the objectBench
	 * 
	 * @return	The wanted objectRepresentation
	 */
	private ObjectRepresentation getObjectRepresentation(DebuggerObject object, boolean inObjectBench){
		for(ObjectRepresentation objectRepresentation: objects){
			if(objectRepresentation.getObject().equals(object)){
				return objectRepresentation;
			}
		}
		return new ObjectRepresentation(object, inObjectBench);
	}
	
	/**
	 * Return a list of all the ObjectRepresentations present in the objectbench or
	 * refered by an object in the objectbench.
	 * 
	 * @return	A list of all the ObjectRepresentations present in the objectbench or
	 * 			refered by an object in the objectbench.
	 */
	public HashSet<ObjectRepresentation> getObjectRepresentations() {
		return objects;
	}
	
	/**
	 * Filters the current list of objects with the given filter.
	 * 
	 * @param	filter
	 * 			| The regular expression by which the objects must be filtered.
	 * @return	The objects that are left after filtering.
	 */
	public HashMap<String,List<FieldInfo>> filter(String regex) {
		if (regex.startsWith("*")) {
			buildAllFieldLists(true);
			regex = regex.substring(1);
		} else
			buildAllFieldLists(false);
		return new StartFilter(regex, objects).join();
	}
	
	/**
	 * Return the ObjectRepresentation of object.
	 * 
	 * @param	object
	 * 			| The object of which the ObjectRepresentation must be returned.
	 * @return	The ObjectRepresentation of object.
	 */
	public ObjectRepresentation getObjectRepresentation(DebuggerObject object){
		for(ObjectRepresentation currentObject: objects){
			if(currentObject.getObject().equals(object)){
				return currentObject;
			}
		}
		return null;
	}
	
	/**
	 * Build all the fieldLists of the objects in the objectbench or referred by an
	 * object on the objectbench.
	 * 
	 * @param	toString
	 * 			| Whether arrays must be presented as a String or as an object.
	 */
	public void buildAllFieldLists(boolean toString){
		for(ObjectRepresentation object: objects){
			if (object.getObject().isArray()) {
				if (!toString) {
					object.removeNullReferences();
					object.buildArrayFieldList();
				} else // valse referer geven zodat array-object niet getekend wordt
					object.addReference(null, null);
			}
			else
				object.buildFieldList(toString);
		}
	}
	
}